package fidl;

import java.util.Collections;
import java.util.Comparator;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.DefaultParser;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Diagnostic;
import org.eclipse.xtext.resource.XtextResourceSet;
import org.franca.core.dsl.FrancaIDLStandaloneSetup;

import com.google.inject.Injector;

public class Cli {
	private void usage(Options options) {
		HelpFormatter formatter = new HelpFormatter();
		formatter.printHelp("java fidl.Cli [OPTIONS] FILES", options);
		System.out.println("Extension options:");
		System.out.println(" TreatEnumAsInt=<true|false> default false");
		System.out.println(" TreatTypedefAsMessage=<true|false> default true");
		System.exit(-1);
	}

	private boolean parseExtensionOptions(String[] options, Configure config) {
		for (String option : options) {
			String[] pair = option.split("=");
			if (pair.length != 2) {
				System.out.println("Unknown option: " + option);
				return false;
			}
			if ("TreatEnumAsInt".equalsIgnoreCase(pair[0])) {
				if ("true".equalsIgnoreCase(pair[1])) {
					config.TreatEnumAsInt = true;
				} else if ("false".equalsIgnoreCase(pair[1])) {
					config.TreatEnumAsInt = false;
				} else {
					System.out.println("Unknown option argument: " + option);
					return false;
				}
			} else if ("TreatTypedefAsMessage".equalsIgnoreCase(pair[0])) {
				if ("true".equalsIgnoreCase(pair[1])) {
					config.TreatTypedefAsMessage = true;
				} else if ("false".equalsIgnoreCase(pair[1])) {
					config.TreatTypedefAsMessage = false;
				} else {
					System.out.println("Unknown option argument: " + option);
					return false;
				}
			} else {
				System.out.println("Unknown option: " + option);
				return false;
			}
		}
		return true;
	}

	private void Main(String[] args) {
		DefaultParser parser = new DefaultParser();
		Options options = new Options();
		options.addOption("h", "help", false, "Show this information and exit;");
		options.addOption("d", "directory", true, "Set output directory;");
		options.addOption("n", "namespace", true, "Set prefix namespace;");
		options.addOption("o", "option", true, "Set extension options;");
		options.addOption("p", "proto", false, "Generator proto .proto file;");
		options.addOption("s", "struct", false, "Generator c struct .h file;");
		CommandLine commands = null;
		try {
			commands = parser.parse(options, args);
		} catch (Exception e) {
			usage(options);
		}
		if (commands.hasOption('h')) {
			usage(options);
		}
		if (commands.getArgs().length == 0) {
			System.out.println("error: Need parameter: FILES.");
			usage(options);
		}
		Configure config = new Configure();
		if (commands.hasOption('o')) {
			if (!parseExtensionOptions(commands.getOptionValues('o'), config)) {
				usage(options);
			}
		}
		if (commands.hasOption('n')) {
			config.PrefixNamespace = commands.getOptionValue('n');
			if (config.PrefixNamespace.matches("(\\w\\.)*\\w")) {
				System.out.println("Prefix namespace format wrong.");
				System.exit(-1);
			}
		}
		String directory = "./";
		if (commands.hasOption('d')) {
			directory = commands.getOptionValue('d');
		}
		FrancaIDLStandaloneSetup setup = new FrancaIDLStandaloneSetup();
		Injector injector = setup.createInjectorAndDoEMFRegistration();
		XtextResourceSet rsset = injector.getProvider(XtextResourceSet.class).get();
		for (String filepath : commands.getArgs()) {
			URI root = URI.createURI("classpath:/");
			URI loc = URI.createFileURI(filepath);
			// resolve the input uri, in case it is a relative path
			URI absURI = loc.resolve(root);
			if (!loc.equals(absURI)) {
				// add this pair to URI converter so that others can get the
				// URI by its relative path
				rsset.getURIConverter().getURIMap().put(loc, absURI);
			}
			Resource resource = null;
			try {
				if (!"fidl".equalsIgnoreCase(absURI.fileExtension())) {
					System.out.println("Unsupport file: " + filepath);
					continue;
				}
				resource = rsset.getResource(absURI, true);
				resource.load(Collections.EMPTY_MAP);
				StoreGenerator storeGenerator = new StoreGenerator();
				JsonGenerator jsonGenerator = new JsonGenerator();
				boolean success = storeGenerator.doGenerate(resource, config) &&
								  jsonGenerator.doGenerate(resource, config);
				EList<Diagnostic> errors = resource.getErrors();
				if (success && errors.size() == 0) {
					storeGenerator.writeToFile(directory);
					jsonGenerator.writeToFile(directory);
				}
				errors.addAll(resource.getWarnings());
				errors.sort(Comparator.comparing(Diagnostic::getLine)
									  .thenComparing(Diagnostic::getColumn));
				for (Diagnostic error : errors) {
					System.out.println(filepath + ":" + error.getLine() + ":" +
						error.getColumn() + ": error: " + error.getMessage());
				}
			} catch (Exception e) {
				System.out.println(e.getMessage());
			} finally {
				if (resource != null) {
					resource.unload();
					rsset.getResources().clear();
				}
			}
		}
	}

	public static void main(String[] args) {
		Cli cli = new Cli();
		cli.Main(args);
	}
}
